package snake.ui;

import snake.common.Constant;
import snake.common.GameContextHolder;
import snake.enums.GameState;
import snake.factory.FoodFactory;
import snake.model.Snake;
import snake.utils.GameUtils;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class GamePanel extends Panel {
    // 定义双缓存图片，解决重回闪烁问题
    private Image offScreenImage;


    public GamePanel() {
        this.setSize(Constant.DEFAULT_WINDOW_WIDTH, Constant.DEFAULT_WINDOW_HEIGHT);
        this.addKeyListener(new SpaceAdapter());
        Timer timer = new Timer(Constant.TIMER_DELAY, new TimerAction());
        GameContextHolder.getContext().setTimer(timer);
    }

    /**
     * 现在缓存图片上绘制，再将完整的图片绘制到界面中。
     * 只需要绘制一次即可，解决重回闪烁
     *
     * @param g the specified Graphics window
     */
    @Override
    public void paint(Graphics g) {
        if (offScreenImage == null) {
            offScreenImage = this.createImage(Constant.DEFAULT_WINDOW_WIDTH, Constant.DEFAULT_WINDOW_HEIGHT);
        }
        // 获取图片对应的graphics对象
        Graphics gImage = offScreenImage.getGraphics();
        // 背景颜色
        gImage.setColor(Constant.BACKGROUND_COLOR);
        gImage.fillRect(0, 0, Constant.DEFAULT_WINDOW_WIDTH, Constant.DEFAULT_WINDOW_HEIGHT);


        Snake snake = GameContextHolder.getContext().getSnake();
        // 绘制蛇身体
        for (int i = snake.getSnakeBodyList().size() - 1; i >= 0; i--) {
            snake.getSnakeBodyList().get(i).paint(gImage);
        }
        // 绘制蛇头
        snake.getSnakeHead().paint(gImage);
        // 食物绘制
        GameContextHolder.getContext().getFood().paint(gImage);
        // 分数绘制
        GameUtils.drawWord(gImage, "" + GameContextHolder.getContext().getScore(), Color.WHITE, new Font("Mono", Font.PLAIN, 14), 10, 20);
        // 绘制提示语
        GameUtils.prompt(gImage);
        // // 将双缓存图片绘制到窗口中
        g.drawImage(offScreenImage, 0, 0, null);
    }

    /**
     * 空格事件处理
     */
    private class SpaceAdapter extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() != KeyEvent.VK_SPACE) {
                GameContextHolder.getContext().getSnake().getSnakeHead().changeDirection(e.getKeyCode());
                return;
            }

            // 空格修改游戏状态，在定时器中会监听到并做出相应的处理
            switch (GameContextHolder.getContext().getState()) {
                case NOT_STATE:
                case PAUSED:
                    GameContextHolder.getContext().setState(GameState.RUNNING);
                    break;
                case RUNNING:
                    GameContextHolder.getContext().setState(GameState.PAUSED);
                    repaint();
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * 定时器处理
     */
    private class TimerAction extends AbstractAction {

        @Override
        public void actionPerformed(ActionEvent e) {
            // 定时器监听游戏状态，并做出相应的处理
            if (GameContextHolder.getContext().getState() == GameState.RUNNING) {
                GameContextHolder.getContext().getSnake().move();
                if (GameUtils.isEatFood()) {
                    GameContextHolder.getContext().getSnake().eatFood();
                    GameContextHolder.getContext().setFood(FoodFactory.getFood());
                }
                if (GameUtils.isGameOver()) {
                    GameContextHolder.getContext().setState(GameState.FAILED);
                    JOptionPane.showMessageDialog(null, "得分：" + GameContextHolder.getContext().getScore(), "游戏结束！", JOptionPane.INFORMATION_MESSAGE);
                }
            }
            if (GameContextHolder.getContext().getState() == GameState.FAILED) {
                GameContextHolder.getContext().setSnake(new Snake());
                GameContextHolder.getContext().setScore(0);
                GameContextHolder.getContext().setLevel(0);
                GameContextHolder.getContext().setState(GameState.NOT_STATE);
            }

            // 定时器每走一次，都需要更新界面重画
            repaint();
        }
    }
}
